<?php
/**
 * Environment.php - 27 Jan 2009
 * 
 * @author Kevin Breed
 */
 
require_once( 'KB/Environment.php' );
 
abstract class KB_Bootstrap_Environment_Abstract extends KB_Environment
{
	/**
	 * @var array 	A definition of the dependencies between parts and paths, 
	 *				which must be set through the abstract method 
	 *				_setPartToPathsDependencies().
	 */	
	public $partToPathsDependencies = array();
	
	/**
	 * @var array 	A definition of the dependencies between paths and paths, 
	 *				which must be set through the abstract method 
	 *				_setPathToPathsDependecies()
	 */	
	public $pathToPathsDependencies = array();
	
	
	/**
	 * @var string	The name of the project folder
	 */
	protected $_projectDirName;
	

	
	/**
	 * @var string	The name of the application directory
	 */
	protected $_appDirName = 'app';
	
	/**
	 * @var string	The name of the config directory
	 */
	protected $_configDirName = 'config';
	
	
		
	/**
	 * @var string	The path to the application directory
	 */
	protected $_appPath;
	
	/**
	 * @var bool	Indication whether $_appPath is set or not
	 */
	protected $_appPathSetFlag = false;
	
	
	/**
	 * @var string	The path to the library directory
	 */
	protected $_libPath;
	
	/**
	 * @var bool	Indication whether $_libPath is set or not
	 */
	protected $_libPathSetFlag = false;
	
	
	/**
	 * @var string	The path to the library config directory
	 */
	protected $_libConfigPath;
	
	/**
	 * @var bool	Indication whether $_libConfigPath is set or not
	 */
	protected $_libConfigPathSetFlag = false;
	
	/**
	 * @var string	The path to the application config directory
	 */
	protected $_appConfigPath;
	
	/**
	 * @var bool	Indication whether $_appConfigPath is set or not
	 */
	protected $_appConfigPathSetFlag = false;

	
	
	/**************/
	/* EXCEPTIONS */
	/**************/
	
	/**
	 * @var int Exception code: Directory does not exists
	 */
	const E_UNEXISTENT_DIR 	= 1;
	
	/**
	 * @var int Exception code: File does not exists
	 */
	const E_UNEXISTENT_FILE = 2;
	
	/**
	 * @var int Exception code: Compose method does not exists
	 */
	const E_UNEXISTENT_COMPOSE_METHOD = 3;
	
		
	/***********************/
	/* GETTERS AND SETTERS */
	/***********************/
	
	/**
	 * Sets a new project directory name and update dependent paths.
	 * 
	 * @param string $name The new name of the project directory
	 */
	public function setProjectDirName ( $name ) 
	{
		$this->_projectDirName = $name;
		$this->recomposeDependentPaths( 'projectDirName' );
	}
	
	/**
	 * @return string The name of the project directory
	 */
	public function getProjectDirName () 
	{
		return $this->_projectDirName;	
	}
	
	
	/**
	 * Sets a new application directory name and recompose dependent parts.
	 * 
	 * @param string $name The new name of the application directory
	 */
	public function setApplicationDirName ( $name ) 
	{
		$this->_appDirName = $name;
		$this->recomposeDependentPaths( 'applicationDirName' );
	}
	
	/**
	 * @return string The name of the application directory
	 */
	public function getApplicationDirName () 
	{
		return $this->_appDirName;	
	}
	
	
	/**
	 * Sets a new config directory name and updates the application and library
	 * config path, if they are not yet set.
	 * 
	 * @param string $name The new name of the config directory
	 */
	public function setConfigDirName ( $name ) 
	{
		$this->_configDirName = $name;
		$this->recomposeDependentPaths( 'configDirName' );
	}
	
	/**
	 * @return string The name of the config directory
	 */
	public function getConfigDirName () 
	{
		return $this->_configDirName;
	}
	
	
	/**
	 * Set the path to the application relative from the document root. Also
	 * updates dependent paths that are not set from outside this class.
	 * 
	 * @param	string	$appPath		The path to the application directory 
	 * 									relative from the document root.
	 * @return 	void
	 */
	public function setApplicationPath ( $appPath )
	{
		$path = $this->docRoot . $appPath;
		
		if ( !file_exists( $path ) && !is_dir( $path ) )
		{
			throw new Exception( "The path '$appPath' is an invalid " .
								 "application path", 
								 self::E_UNEXISTENT_DIR );
			return;
		}
		
		$this->_appPath 		= $path;
		$this->_appPathSetFlag 	= true;
		
		$this->recomposeDependentPathsByPath( 'appPath' );
	}
	
	/**
	 * Returns the application path.
	 * 
	 * @return string	The application path
	 */
	public function getApplicationPath ()
	{
		return $this->_appPath;
	}
	
	
	/**
	 * Set the path to the library relative from the document root and updates
	 * dependent paths that are not set from outside this class.
	 * 
	 * @param 	string	$libPath		The path to the library relative from
	 * 									the document root.
	 * @return	void
	 */
	public function setLibraryPath ( $libPath )
	{
		$path = $this->docRoot . $libPath;
		
		if ( !file_exists( $path ) && !is_dir( $path ) )
		{
			throw new Exception( "The path '$libPath' is an invalid " .
								 "library path", 
								 self::E_UNEXISTENT_DIR );
			return;
		}
		
		$this->_libPath 		= $path;
		$this->_libPathSetFlag 	= true;
		
		$this->recomposeDependentPathsByPath( 'libraryPath' );
	}
	
	/**
	 * Returns the library path.
	 * 
	 * @return string	The library path
	 */
	public function getLibraryPath ()
	{
		return $this->_libPath;
	}
	
	
	/**
	 * Determine path to the application config file and updates dependent paths 
	 * that are not set from outside this class.
	 * 
	 * @param 	string	$path		The path to the application config file 
	 * 								relative from the document root, without
	 * 								trailing slash.
	 * @return	void
	 */
	public function setApplicationConfigPath ( 	$appConfigPath = null )
	{		
		$path = $this->docRoot . $appConfigPath;
		
		if ( !file_exists( $path ) && !is_dir( $path ) )
		{
			throw new Exception( "The path '$path' is an invalid " .
								 "library path", 
								 self::E_UNEXISTENT_DIR );
			return;
		}
		
		$this->_appConfigPath 			= $path;
		$this->_appConfigPathSetFlag 	= true;
		
		$this->recomposeDependentPathsByPath( 'applicationConfigPath' );
	}
	
	/**
	 * Returns the complete path to the application config file
	 * 
	 * @return	string	The path to the application config file
	 */
	public function getApplicationConfigPath ()
	{
		return $this->_appConfigPath;
	}
	
	
	/**
	 * Determine path to the application config file and updates dependent paths 
	 * that are not set from outside this class.
	 * 
	 * @param 	string	$path		The path to the application config file 
	 * 								relative from the document root, without
	 * 								trailing slash.
	 * @return	void
	 */
	public function setLibraryConfigPath ( $libraryConfigPath = null )
	{
		$path = $this->docRoot . $libraryConfigPath;
		
		if ( !file_exists( $path ) && !is_dir( $path ) )
		{
			throw new Exception( "The path '$path' is an invalid " .
								 "library path", 
								 self::E_UNEXISTENT_DIR );
			return;
		}
		
		$this->_libConfigPath 			= $path;
		$this->_libConfigPathSetFlag 	= true;
		
		$this->recomposeDependentPathsByPath( 'libraryConfigPath' );
	}
	
	/**
	 * Returns the path to the application config file
	 * 
	 * @return	string	The path to the application config file
	 */
	public function getLibraryConfigPath ()
	{
		return $this->_libConfigPath;
	}	
	
	
	/*********************/
	/* MACHINERY HANDLES */
	/*********************/
	
	/**
	 * Constructor
	 */
	public function __construct ()
	{
		// Initialise base paths and names
		$this->_initProjectDirName();
		$this->_initPartToPathsDependencies();
		$this->_initPathToPathsDependencies();
		$this->_composeAppPath();
		$this->_composeAppConfigPath();
		//$this->_composeLibPath();
		//$this->_composeLibConfigPath();
	}
	
	
	/**
	 * Returns the paths of which the specified part is a constitutive element.
	 *
	 * @param 	string 		$part 	The part 
	 * @return 	array|bool			The dependent paths, or false when the 
	 *								specified path doesn't have a dependent path
	 */
	public function getDependentPathsByPart( $part )
	{
		if ( array_key_exists( $part, $this->partToPathsDependencies ) )
			return $this->_partToPathsDependencies[ $part ];
		
		return false;
	}
	
	/**
	 * Returns the paths of which the specified path is a constitutive element.
	 *
	 * @param 	string 		$path 	The path 
	 * @return 	array|bool			The dependent paths, or false when the 
	 *								specified path doesn't have a dependent path
	 */
	public function getDependentPathsByPath( $path )
	{
		if ( array_key_exists( $part, $this->pathToPathsDependencies ) )
			return $this->_pathToPathsDependencies[ $part ];
		
		return false;
	}
	
	/**
	 * Recomposes all the paths of which the specified part is a constitutive 
	 * element.
	 *
	 * @param 	string 	$part 				The part
	 * @param	bool	$overWriteSetPaths	If set to true the recomposement 
	 *										will overwrite values that are 
	 *										already set outside of this class.
	 * @return	void
	 */
	public function recomposeDependentPathsByPart( $part, $overwriteSetPaths=false )
	{
		$depParts = $this->getDependentPathsByPart( $part );
		
		if ( $depParts )
			 $this->_recomposeDependentPaths( $depParts, $overwriteSetPaths );
	}
	
	/**
	 * Recomposes all the paths of which the specified path is a constitutive 
	 * element.
	 *
	 * @param 	string 	$path 				The path
	 * @param	bool	$overWriteSetPaths	If set to true the recomposement 
	 *										will overwrite values that are 
	 *										already set outside of this class.
	 * @return	void
	 */
	public function recomposeDependentPathsByPath ( $path, $overwriteSetPaths=false )
	{
		$depParts = $this->getDependentPathsByPath( $path );
		
		if ( $depPaths )
			 $this->_recomposeDependentPaths( $depPaths, $overwriteSetPaths );
	}
	
	
	/*************/
	/* MACHINERY */
	/*************/
	
	/**
	 * Recomposes all the given paths.
	 *
	 * @param 	array 	$paths 				The paths to recompose
	 * @param	bool	$overWriteSetPaths	If set to true the recomposement 
	 *										will overwrite values that are 
	 *										already set outside of this class.
	 * @return	void
	 */
	protected function _recomposeDependentPaths( $depPaths, $overWriteSetPaths=false )
	{
		// call compose methods
		foreach ( $depPaths as $path )
		{
			$fl = substr( $path, 0, 1 ); 	// first letter
			$ol = substr( $path, 1 );		// other letters
			$sf = $path . 'SetFlag';		// set flag
			$mn = '_compose' . strtoupper( $fl ) . $ol; // method name
			
			// oke to recompose?
			if ( $overwriteSetPaths || !${$sf} )
			{
				// call method if it exists
				if ( method_exists( $this, $mn ) ) 
					 call_user_func( array( $this, $mn ) );
			
				// or throw an exception
				else
					throw new ErrorException
					( 
						"Unable to recompose '$ol'. '$mn' does not exists.", 
						self::E_UNEXISTENT_COMPOSE_METHOD,
						E_WARNING 
					);
			}
		}
	}
	
	abstract protected function _initProjectDirName();
	abstract protected function _initPartToPathsDependencies();
	abstract protected function _initPathToPathsDependencies();
	abstract protected function _composeAppPath();
	abstract protected function _composeAppConfigPath();
	//abstract protected function _composeLibPath();
	//abstract protected function _composeLibConfigPath();
}

?>
